﻿// ParseLib - An open source library for natural language processing.
// Copyright (C) Kais Dukes, 2012. E-mail: kais@kaisdukes.com
// http://parselib.org

using System;

namespace ParseLib.Tagging.Brill.Rules
{
    public class SingleTagRule : Rule
    {
        public PartOfSpeech Context { get; set; }

        public override bool Match(Sentence sentence, int index)
        {
            // Change allowed?
            var token = sentence[index];
            if (token.CurrentTag != From || !token.CanTag(To)) return false;

            // Match context
            switch(RuleType)
            {
                case RuleType.Previous:
                    return MatchContext(sentence, index - 1);
                case RuleType.OneOfPreviousTwo:
                    return MatchContext(sentence, index - 1)
                        || MatchContext(sentence, index - 2);
                case RuleType.OneOfPreviousThree:
                    return MatchContext(sentence, index - 1)
                        || MatchContext(sentence, index - 2)
                        || MatchContext(sentence, index - 3);
                default:
                    throw new NotImplementedException();
            }
        }

        private bool MatchContext(Sentence sentence, int index)
        {
            var context = sentence[index];
            return (context != null ? context.CurrentTag : null) == Context;
        }

        public override void Apply(Sentence sentence)
        {
            // Apply the rule.
            var size = sentence.TokenCount;
            for (var i = 0; i < size; i++)
            {
                // Match?
                if (Match(sentence, i))
                {
                    // Apply.
                    sentence[i].CurrentTag = To;
                }
            }
        }

        public override bool Equals(object value)
        {
            var rule = value as SingleTagRule;
            return rule != null && Equals(rule);
        }

        public bool Equals(SingleTagRule rule)
        {
            if (rule == null) return false;
            if (rule == this) return true;
            return Equals(rule.RuleType, RuleType)
                   && Equals(rule.From, From)
                   && Equals(rule.To, To)
                   && Equals(rule.Context, Context);
        }

        public override int GetHashCode()
        {
            var hashCode = 17;
            hashCode = hashCode*23 + RuleType.GetHashCode();
            hashCode = hashCode*23 + From.GetHashCode();
            hashCode = hashCode*23 + To.GetHashCode();
            hashCode = hashCode*23 + (Context != null ? Context.GetHashCode() : 0);
            return hashCode;
        }

        public override string ToString()
        {
            return string.Format(
                "Change {0} to {1} when {2} is {3}",
                From, To, FormatRuleType(RuleType), FormatTag(Context));
        }

        private static string FormatTag(PartOfSpeech tag)
        {
            return tag != null ? tag.Symbol : "<none>";
        }
    }
}